include("autorun/server/StargateExtras.lua")
include("shared.lua")

local Planets = {}

for _, logicEnt in pairs(ents.FindByClass("logic_case")) do
   if(logicEnt:GetKeyValues()["Case01"] == "planet") then
      table.insert(Planets, logicEnt)
   end
end

ENT.immuneEnts = {}
ENT.radius = 0
ENT.cycleInterval = 0.1

ENT.Sounds = {}

function ENT:Initialize()
   self.disintegrator = ents.Create("env_entity_dissolver") 
   self.disintegrator:SetKeyValue("magnitude", "1") 
   self.disintegrator:SetKeyValue("dissolvetype", "0") 
   self.disintegrator:Spawn() 
   self.disintegrator:Activate()
end

function ENT:Setup(dakaraWeapon, centre, immuneEnts)
   self.Weapon = dakaraWeapon
   self.Sounds["ambient"] = CreateSound(self.Entity, self.Weapon.Sounds.Paths["firing"])
   
   self:SetPos(centre)
   
   self.immuneEnts = immuneEnts or {}
end

function ENT:Start()
   self:SpawnEffect()
   
   local ambientSound = self.Sounds["ambient"]
   
   if(ambientSound) then
      ambientSound:Play()
   end
   
   return true
end

function ENT:SpawnEffect()
   local waveDuration = (self.Weapon.Wave.MAX_RADIUS / self.Weapon.Wave.EXPANSION_RATE) * 
                        self.cycleInterval

   local effectInfo = EffectData()
   effectInfo:SetOrigin(self:GetPos())
   effectInfo:SetMagnitude(self.Weapon.Wave.EXPANSION_RATE / self.cycleInterval)
   effectInfo:SetRadius(CurTime() + self.cycleInterval)
   effectInfo:SetScale(waveDuration)
   
   util.Effect("dakara_wave", effectInfo)
end

function ENT:ExpandWave()
   self.radius = self.radius + self.Weapon.Wave.EXPANSION_RATE
   
   if(self.radius > self.Weapon.Wave.MAX_RADIUS) then
      self:End()
      return false
   end
   
   self:DisintegrateTargets()
   self:HitShields()
   
   return true
end

function ENT:DisintegrateTargets()   
   for _, entity in pairs(ents.FindInSphere(self:GetPos(), self.radius)) do
      local isValidTarget = (self:IsValidTarget(entity) && entity:GetPhysicsObject():IsValid()) -- So it doesn't dissolve stuff like info player start.
      
      if(isValidTarget == true) then
         for _, immuneEnt in pairs(self.immuneEnts) do
            if(entity == immuneEnt) then
               isValidTarget = false
               break
            end
         end
         
         local isProtectedByShield = self.Weapon.IS_SHIELD_PIERCING == false && 
                                     StargateExtras:IsEntityShielded(entity) == true
         
         if(isProtectedByShield == false) then
            if(isValidTarget && self:IsEntityATarget(entity)) then
               Msg("dakara_wave: destroying ", entity, "\n")
            
               if(entity.Rep_AI_Disassemble) then
                  entity:Rep_AI_Disassemble()
               elseif(entity:IsPlayer()) then
                  entity:Kill()
               else
                  self.disintegrator:SetPos(entity:GetPos()) 
                  entity:SetName(tostring(entity)) 
                  self.disintegrator:Fire("Dissolve", entity:GetName(), 0)
               end
            elseif(entity.IsStargate) then
               self:PropagateThroughGate(entity)
            end
         end
         
         if(entity:GetClass() ~= "shield" || self.Weapon.IS_SHIELD_PIERCING == true) then
            entity.isHitByDakaraWave = true
            
            local waveDuration = ((self.Weapon.Wave.MAX_RADIUS - self.radius) / 
                                  self.Weapon.Wave.EXPANSION_RATE) * 
                                 self.cycleInterval
            
            timer.Simple(waveDuration,
                         function() 
                            entity.isHitByDakaraWave = false 
                         end,
                         nil)
         end
      end
   end
end

function ENT:PropagateThroughGate(gate)
   if(gate.IsStargate ~= true || 
      StargateExtras:IsStargateOutbound(gate) == false ||
      StargateExtras:IsIrisClosed(gate) == true) then
      return false
   end
   
   local remoteGate = StargateExtras:GetRemoteStargate(gate)
   
   local newWave = ents.Create("dakara_wave")
   
   newWave:Setup(self.Weapon, StargateExtras:GetEntityCentre(remoteGate), {})
   newWave:Spawn()
   newWave:Activate()
   newWave:Start()
   
   return true
end

function ENT:HitShields()
   for _, entity in pairs(ents.FindInSphere(self:GetPos(), self.radius)) do
      if(entity.isHitByDakaraWave ~= true) then
         if(self.Weapon.IS_SHIELD_PIERCING == false && entity:GetClass() == "shield") then
            local distanceToEnt = (entity:GetPos() - self:GetPos()):Length()
            local waveRadiusNeededToEncompassShield = distanceToEnt + entity.Size
         
            if(self.radius >= waveRadiusNeededToEncompassShield) then
               local waveDuration = (self.Weapon.Wave.MAX_RADIUS / self.Weapon.Wave.EXPANSION_RATE) * 
                                    self.cycleInterval
               
               entity.Parent.Strength = 0
               entity.isHitByDakaraWave = true
               
               timer.Simple(waveDuration,
                            function() 
                               entity.isHitByDakaraWave = false 
                            end,
                            nil)
            else
               entity.Parent.Strength = math.ceil(entity.Parent.Strength / 2)
            end
            
            entity:Hit(self.Entity, entity:NearestPoint(self:GetPos()))
         end
      end
   end
end

function ENT:IsEntityATarget(entity)
   local entityClass = entity:GetClass()
   
   for _, targetType in pairs(self.Weapon.Targets) do
      if(string.find(entityClass, targetType)) then
         return true
      end
   end
   
   if(string.find(entityClass, "prop")) then
      local entityModel = entity:GetModel()
      
      for _, targetType in pairs(self.Weapon.Targets) do
         if(string.find(entityModel, targetType)) then
            return true
         end
      end
   end
   
   return false
end

function ENT:IsValidTarget(entity)
   local entDistance = entity:GetPos():Distance(self:GetPos())
   local isAtEdgeOfWave = entDistance >= self.radius - (self.Weapon.Wave.EXPANSION_RATE * 2)
   local isInSafeZone = entDistance <= self.Weapon.safeZoneRadius
   local hasParent = ValidEntity(entity:GetParent())

   return isAtEdgeOfWave == true &&
          isInSafeZone == false &&
          entity.isHitByDakaraWave ~= true &&
          entity:IsWorld() == false &&
          hasParent == false &&
          self:IsPlanet(entity) == false
end

function ENT:IsPlanet(entity)
   for _, planet in pairs(Planets) do
      if(planet:GetPos() == entity:GetPos()) then
         return true
      end
   end
   
   return false
end

function ENT:End()
   for _, entity in pairs(ents.FindInSphere(self:GetPos(), self.radius)) do
      entity.isHitByDakaraWave = false
   end
   
   self.radius = 0
   
   if(self.Sounds["ambient"]) then
      self.Sounds["ambient"]:Stop()
   end
   
   return true
end

function ENT:Think()
   self:NextThink(CurTime() + self.cycleInterval)
   
   local isWaveExpanding = self:ExpandWave()

   if(isWaveExpanding) then
      return true
   else
      self:Remove()
      return false
   end
end

function ENT:OnRemove()
   self:End()
   
   self.disintegrator:Remove()
end
